package com.example.demo;

import com.example.api.JobApi;
import com.example.api.JobExecutor;
import com.example.api.dto.ExecuteUserDTO;
import com.example.api.dto.TiggerDTO;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import io.undertow.io.Receiver;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.Headers;
import io.undertow.util.StatusCodes;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Map;

/**
 * @author fanhang
 */
public class UndertowHandler implements HttpHandler {
    private static final String APPLICATION_JSON = "application/json";
    private final BodyCallback bodyCallback;

    public UndertowHandler(JsonMapper jsonMapper, JobExecutor jobExecutor) throws JsonProcessingException {
        this.bodyCallback = new BodyCallback(jsonMapper, jobExecutor);
    }

    @Override
    public void handleRequest(HttpServerExchange exchange) throws Exception {
        exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, APPLICATION_JSON);
        exchange.getRequestReceiver().receiveFullBytes(bodyCallback);
    }

    private static class BodyCallback implements Receiver.FullBytesCallback {
        private final ByteBuffer byteBuffer404;
        private final JsonMapper jsonMapper;
        private final JobExecutor jobExecutor;

        public BodyCallback(JsonMapper jsonMapper, JobExecutor jobExecutor) throws JsonProcessingException {
            this.jsonMapper = jsonMapper;
            this.jobExecutor = jobExecutor;
            this.byteBuffer404 = ByteBuffer.wrap(jsonMapper.writeValueAsBytes(Map.of("message", "404")));
        }

        @Override
        public void handle(HttpServerExchange exchange, byte[] message) {
            try {
                Object data = this.dispatch(exchange.getRequestURI(), message);
                this.write(exchange, StatusCodes.OK, data);
            } catch (UnsupportedOperationException e) {
                this.write(exchange, StatusCodes.NOT_FOUND, byteBuffer404);
            } catch (Exception e) {
                this.write(exchange, StatusCodes.INTERNAL_SERVER_ERROR, Map.of("message", e.getMessage()));
            }
        }

        private Object dispatch(String uri, byte[] data) {
            if (uri.endsWith("/")) {
                uri = uri.substring(0, uri.length() - 1);
            }
            if (uri.startsWith(JobApi.EXECUTE_USER)) {
                return this.jobExecutor.executeUser(decode(data, ExecuteUserDTO.class));
            }
            if (uri.startsWith(JobApi.TIGGER)) {
                return this.jobExecutor.tigger(decode(data, TiggerDTO.class));
            }
            throw new UnsupportedOperationException("不支持");
        }

        private <T> T decode(byte[] data, Class<T> clazz) {
            try {
                return jsonMapper.readValue(data, clazz);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        private void write(HttpServerExchange httpServerExchange, int status, Object data) {
            try {
                this.write(httpServerExchange, status, ByteBuffer.wrap(jsonMapper.writeValueAsBytes(data)));
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }

        private void write(HttpServerExchange httpServerExchange, int status, ByteBuffer byteBuffer) {
            httpServerExchange.setStatusCode(status);
            httpServerExchange.getResponseSender().send(byteBuffer);
        }
    }
}
